﻿using Microsoft.Playwright;
using OnlineStore.Common;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Xml.Linq;

namespace OnlineStore
{
    public class TaoBaoReceiver: Receiver
    {
        public TaoBaoReceiver(Player player) : base(player)
        {
        }

        bool isOk;
        IBrowserContext myContext;
        List<IPage> pages = new List<IPage>();

        public async override Task Execute(IBrowser browser)
        {
            string contents = 
                 "******************************************\n" +
                $"时间：{DateTime.Now.ToString("yyyy-MM-dd HH：mm：ss：ffff")}\n" +
                $"网店类型：{Player.Type}\n" +
                $"网店账号：{Player.User}\n" +
                $"发布数量：{Player.Count}\n" +
                $"模板宝贝：{Player.ModelBaby}\n" +
                $"标题文件：{Player.TitlePath}\n" +
                $"TitleList：{Player.TitleList.Count}\n" +
                $"CarMap：{Player.CarMap.Count}\n" +
                 "******************************************";

            await Print(contents);

            try
            {
                myContext =  await GetContext(browser);
            }
            catch (Exception e)
            {
                await Print(e.StackTrace);
                throw;
                
            }


            await Print("打开新浏览器页签");
            // 打开新浏览器页签
            var page = await myContext.NewPageAsync();
            PageNavigation pgNavigation = new PageNavigation(page);
            pgNavigation.PrintAction = Player.DetailedAction;
            pages.Add(page);

            await Print("屏蔽 Webdriver 检测");
            //// 屏蔽 Webdriver 检测
            string js = "Object.defineProperties(navigator,webdriver,{get:()=>false});";
            await page.AddInitScriptAsync(js);

            await Print("打开 淘宝网址");
            await page.GotoAsync(Player.GetPath("淘宝网址"));

            await Print("判断是否使用登录记录");
            await Print($"Cookie：{Player.Cookie}");

            try
            {
                if (Player.MyStatus.Status) 
                {
                    await Closed();
                    return;
                }

                if (string.IsNullOrEmpty(Player.Cookie)) await OpenLogin(page);
                else await OpenLoginForCookie(page);
            }
            catch (Exception e)
            {
                await Screenshot(page, "登录_异常");
                await Closed();
                await Print(e.StackTrace);
                throw;
            }
            

            await Print("检查 登录验证码页面");
            try
            {
                await CheckVerification(page);

            }
            catch (Exception e)
            {
                await Screenshot(page, "登录验证码页面_异常");
                await Closed();
                await Print(e.StackTrace);
                throw;
            }

            await Print("检查 频繁登录窗口");
            try
            {
                await CheckFrequently(page);
            }
            catch (Exception e)
            {
                await Screenshot(page, "1检查频繁登录窗口_异常");
                await Closed();
                await Print(e.StackTrace);
                throw;
            }

            await Print("点击 商品");
            try
            {
                //await page.Locator(Player.GetPath("点击商品")).ClickAsync();
                isOk = await pgNavigation.PageGoQSele("Click", Player.GetPath("点击商品"));
                if (!isOk) return;

                if (Player.MyStatus.Status) { await Closed(); return; }
            }
            catch (Exception e)
            {
                await Screenshot(page,"点击商品步骤_异常");
                await Closed();
                await Print(e.StackTrace);
                throw;
            }

            await Print("点击 宝贝ID搜索框");
            //await page.Locator(Player.GetPath("宝贝ID搜索框")).ClickAsync();
            isOk = await pgNavigation.Click(Player.GetPath("宝贝ID搜索框"));
            if (!isOk) { await Closed(); return; }

            await Print("输入 宝贝ID搜索框 - 模板宝贝");
            //await page.Locator(Player.GetPath("宝贝ID搜索框")).FillAsync(Player.ModelBaby);
            isOk = await pgNavigation.Fill(Player.GetPath("宝贝ID搜索框"), Player.ModelBaby);
            if (!isOk) { await Closed(); return; }

            await Print("点击 宝贝搜索按钮");
            //await page.Locator(Player.GetPath("宝贝搜索按钮")).ClickAsync();
            isOk = await pgNavigation.Click(Player.GetPath("宝贝搜索按钮"));
            if (!isOk) { await Closed(); return; }

            await Print("------获取剩余标题对应的图片名列表------");
            var picKey = Player.CarMap.Keys.ToList<string>();


            var watch = new Stopwatch();
            for (int i = 0; i < Player.Count; i++)
            {
                await Print($"------Status【{Player.MyStatus.Status}】------");
                if (Player.MyStatus.Status) { break; }
                watch.Start();
                await Print("点击 发布相似宝贝");
                var page1 = await page.RunAndWaitForPopupAsync(async () =>
                {
                    await page.Locator(Player.GetPath("发布相似宝贝")).First.ClickAsync();
                });

                PageNavigation pgNavigation1 = new PageNavigation(page1);
                pgNavigation1.PrintAction = Player.DetailedAction;
                pages.Add(page1);

                try
                {
                    await Print("清空 标题输入框");
                    //await page1.Locator(Player.GetPath("标题输入框")).ClearAsync();
                    isOk = await pgNavigation.Clear(Player.GetPath("标题输入框"));
                    if (!isOk) { break; }

                    await Print($"输入 标题输入框 - 车型标题\n{Player.CarMap[picKey[i]]}");
                    //await page1.Locator(Player.GetPath("标题输入框")).FillAsync(Player.CarMap[picKey[i]]);
                    isOk = await pgNavigation.Fill(Player.GetPath("标题输入框"), Player.CarMap[picKey[i]]);
                    if (!isOk) { break; }

                    await Sleep(Player.TitleDelay);

                    await Print("点击 宝贝主图框");
                    //await page1.Locator(Player.GetPath("宝贝主图框")).First.ClickAsync();
                    var element = page1.Locator(Player.GetPath("宝贝主图框"));
                    if (element != null) { await element.First.ClickAsync(); }
                    else { break; }


                    await Print("点击 浮窗 - 我的图片");
                    await page1.FrameLocator(Player.GetPath("浮窗")).GetByText(Player.GetPath("我的图片")).ClickAsync();

                    if (i == 0)
                    {
                        await Print("点击 浮窗 - 图片排序规则下拉框");
                        await page1.FrameLocator(Player.GetPath("浮窗")).Locator("div").Filter(new LocatorFilterOptions { HasTextRegex = new Regex(Player.GetPath("按修改时间从晚到早")) }).Locator("i").ClickAsync();

                        await Print("点击 浮窗 - 按修改时间从晚到早");
                        await page1.FrameLocator(Player.GetPath("浮窗")).GetByRole(AriaRole.Menuitem, new FrameLocatorGetByRoleOptions { Name = Player.GetPath("按修改时间从晚到早2") }).ClickAsync();

                        // 首次运行，在日志中写入时间和任务数据
                        Player.UsedTitles.Add(DateTime.Now.ToString("F"));
                        Player.UsedTitles.Add($"网店类型：{Player.Type}");
                        Player.UsedTitles.Add($"网店账号：{Player.User}");
                        Player.UsedTitles.Add($"发布数量：{Player.Count}");
                        Player.UsedTitles.Add($"模板宝贝：{Player.ModelBaby}");
                        Player.UsedTitles.Add($"标题文件：{Player.TitlePath}");
                    }

                    await Print("点击 浮窗 - 图片搜索框");
                    await page1.FrameLocator(Player.GetPath("浮窗")).Locator(Player.GetPath("图片搜索框")).ClickAsync();

                    await Print($"输入 车型图片标题\n{picKey[i]}");
                    await page1.FrameLocator(Player.GetPath("浮窗")).Locator(Player.GetPath("图片搜索框")).FillAsync(picKey[i]);

                    await Print("点击 浮窗 - 图片搜索按钮");
                    await page1.FrameLocator(Player.GetPath("浮窗")).Locator("i").Nth(2).ClickAsync();

                    await Print("点击 浮窗 - 主图第一张");
                    await page1.FrameLocator(Player.GetPath("浮窗")).Locator(Player.GetPath("主图第一张")).ClickAsync();

                    await Print("点击 浮窗 - 图片空间第二个文件夹");
                    await page1.FrameLocator(Player.GetPath("浮窗")).GetByTitle(Player.GetPath("图片空间第二个文件夹"), new FrameLocatorGetByTitleOptions { Exact = true }).ClickAsync();

                    await Print("点击 浮窗 - 主图第二张");
                    await page1.FrameLocator(Player.GetPath("浮窗")).Locator(Player.GetPath("主图第二张")).ClickAsync();

                    await Print("点击 浮窗 - 主图第三张");
                    await page1.FrameLocator(Player.GetPath("浮窗")).Locator(Player.GetPath("主图第三张")).ClickAsync();

                    await Print("点击 浮窗 - 主图第四张");
                    await page1.FrameLocator(Player.GetPath("浮窗")).Locator(Player.GetPath("主图第四张")).ClickAsync();

                    await Print("点击 浮窗 - 主图第五张");
                    await page1.FrameLocator(Player.GetPath("浮窗")).Locator(Player.GetPath("主图第五张")).ClickAsync();

                    await Print("选中 立刻上架按钮");
                    await page1.Locator(Player.GetPath("立刻上架按钮")).First.CheckAsync();

                    await Print("点击 提交宝贝按钮");
                    await page1.Locator(Player.GetPath("提交宝贝按钮")).ClickAsync();

                    //await Print("检查 频繁登录窗口");
                    //try
                    //{
                    //    await CheckFrequently(page1);
                    //}
                    //catch (Exception)
                    //{
                    //    await Screenshot(page1, "2检查频繁登录窗口_异常");
                    //    throw;
                    //}
                }
                catch (Exception e)
                {
                    await Screenshot(page1, "发布过程_异常");
                    await Closed();
                    await Print(e.StackTrace);
                    throw;
                }

                try
                {
                    await Print("点击 发布成功");
                    await page1.Locator(Player.GetPath("发布成功")).ClickAsync();
                }
                catch (Exception e)
                {
                    await Screenshot(page1, "提交宝贝后_异常");
                    await Closed();
                    await Print(e.StackTrace);
                    throw;
                }

                Player.ProgressAction(i + 1, Player.Count);
                await Print($"------删除已经上的标题[{Player.CarMap[picKey[i]]}]------");
                Player.UsedTitles.Add(Player.CarMap[picKey[i]]);
                Player.CarMap.Remove(picKey[i]);
                await Print($"已用标题 UsedTitles: {Player.UsedTitles.Count - 6}");
                await Print($"剩余标题 CarMap: {Player.CarMap.Count}");

                watch.Stop();
                await Print($"单次耗时：{watch.Elapsed}");
                watch.Reset();

                await Print($"-----------------第【{i + 1}】轮结束-----------------");
                await Print("关闭发布成功页面");
                await page1.CloseAsync();
                await Sleep(Player.Delay);
            }

            await Closed();
            await Print($"日志保存地址：{Player.LogPath}");
            await Print($"时间：{DateTime.Now.ToString("yyyy-MM-dd HH：mm：ss：ffff")}");
            await Print($"-----------------【{Player.User}】执行结束 -----------------");
        }

        /// <summary>
        /// 创建内容容器，返回内容容器
        /// </summary>
        async Task<IBrowserContext> GetContext(IBrowser browser)
        {
            int viewportHeight; // 浏览器的高
            int viewportWidth; //浏览器的宽
            string userAgent; // 浏览器请求头
            string cookie = ""; // 登录记录
            IBrowserContext context;

            if (!Int32.TryParse(Player.PwSettingConfig["ViewportSize"][0].ToString(), out viewportHeight))
            {
                viewportHeight = 1080;
            }

            if (!Int32.TryParse(Player.PwSettingConfig["ViewportSize"][1].ToString(), out viewportWidth))
            {
                viewportHeight = 1920;
            }

            ViewportSize viewportSize = new ViewportSize { Height = viewportHeight, Width = viewportWidth };

            userAgent = Player.PwSettingConfig["UserAgent"].ToString();

            if (!string.IsNullOrEmpty(Player.Cookie))
            {
                cookie = Player.Cookie + ".json";
                if ((File.Exists(cookie)))
                {
                    await Print($"使用已保存登录状态 【 {cookie} 】");
                    context = await browser.NewContextAsync(new BrowserNewContextOptions
                    {
                        ViewportSize = viewportSize,
                        UserAgent = userAgent,
                        StorageStatePath = cookie, //使用已经保存的token文件
                    });
                }
                else
                {
                    await Print($"【 {cookie} 】文件不存在，【 不 】使用已保存登录状态");
                    context = await browser.NewContextAsync(new BrowserNewContextOptions
                    {
                        ViewportSize = viewportSize,
                        UserAgent = userAgent,
                    });
                }
            }
            else
            {
                await Print("【 不 】使用已保存登录状态");
                context = await browser.NewContextAsync(new BrowserNewContextOptions
                {
                    ViewportSize = viewportSize,
                    UserAgent = userAgent,
                });
            }

            await Print("******************************************");
            await Print("context 设置：");
            await Print($"ViewportSize：{viewportHeight},{viewportWidth}");
            await Print($"userAgent：{userAgent}");
            await Print($"cookie：{cookie}");
            await Print("******************************************");

            return context;
        }

        /// <summary>
        /// 执行输入登录
        /// </summary>
        async Task OpenLogin(IPage page)
        {
            await Print("输入前 获取登录按钮位置");
            var beforBox = await GetElementBOX(page, Player.GetPath("登录按钮"));

            if (beforBox == null)
            {
                await Print("**************** 查找不到登录按钮 ****************");
                return;
            }

            float beforHight = beforBox.Y;
            await Print($"登录按钮 [初始] 位置为 X: {beforBox.X},Y: {beforBox.Y},H: {beforBox.Height},W: {beforBox.Width}");

            await Print("点击 账号输入框");
            await page.Locator(Player.GetPath("账号输入框")).ClickAsync();

            await Print("输入 账号输入框 - 淘宝账号");
            await page.Locator(Player.GetPath("账号输入框")).FillAsync(Player.User);

            await Print("点击 密码输入框");
            await page.Locator(Player.GetPath("密码输入框")).ClickAsync();

            await Print("输入 密码输入框 - 淘宝密码");
            await page.Locator(Player.GetPath("密码输入框")).FillAsync(Player.Password);

            await Print("输入后 获取登录按钮位置");
            var afterBox = await GetElementBOX(page, Player.GetPath("登录按钮"));

            if (beforBox == null)
            {
                await Print("**************** 查找不到登录按钮 ****************");
                return;
            }

            float afterHight = afterBox.Y;
            await Print($"登录按钮【之后】位置为 X: {afterBox.X},Y: {afterBox.Y},H: {afterBox.Height},W: {afterBox.Width}");

            float newhight;
            if (beforHight != afterHight)
            {
                await Print("****************拖动滑块****************");
                newhight = (afterHight - beforHight) / 2 + beforHight;
                await Print($"newhight {newhight}");
                await page.Mouse.MoveAsync(afterBox.X + 2, newhight);
                await Print($"MoveAsync x: {afterBox.X + 2} , y: {newhight}");
                await page.Mouse.DownAsync();
                await page.Mouse.MoveAsync(afterBox.X + afterBox.Width - 2, newhight);
                await Print($"MoveAsync x: {afterBox.X + afterBox.Width - 2} , y: {newhight}");
                await page.Mouse.UpAsync();
            }

            await Print("点击 登录按钮");
            await page.Locator(Player.GetPath("登录按钮")).ClickAsync();

            // 保存登录记录
            await SaveStorage(Player.Save);
        }

        /// <summary>
        /// 执行快速登录操作，如果没找到快速登录，则使用输入登录
        /// </summary>
        async Task OpenLoginForCookie(IPage page)
        {
            try
            {
                var loginButton = page.Locator(Player.GetPath("快速登录按钮"));
                await Print("点击 快速登录按钮");
                await page.Locator(Player.GetPath("快速登录按钮")).ClickAsync();
                // 保存登录记录
                await SaveStorage(Player.Save);
            }
            catch
            {
                await Print("没有找到快速登录按钮，使用普通登录");
                await OpenLogin(page);
            }
        }

        /// <summary>
        /// 检查是否出现频繁操作滑块
        /// </summary>
        async Task CheckFrequently(IPage page)
        {
            var frequently = await CheckElement(page, Player.GetPath("频繁登录窗口"));
            if (frequently == null) return;
            try
            {
                await Print("****************拖动滑块****************");
                var elementBox = await frequently.BoundingBoxAsync();
                if (elementBox == null) return;
                await Print($"频繁登录窗口 x:{elementBox.X}/w:{elementBox.Width}/y:{elementBox.Y}/h:{elementBox.Height}");
                float beforX = elementBox.X + 5;
                float afterX = beforX + elementBox.Width - 5;
                float y = elementBox.Y + 14;
                await Print($"1.MoveAsync x: {beforX} , y: {y}");
                await Print($"2.MoveAsync x: {afterX} , y: {y}");
                await page.Mouse.MoveAsync(beforX, y);
                await page.Mouse.DownAsync();
                await page.Mouse.MoveAsync(afterX, y);
                await page.Mouse.UpAsync();
            }
            catch
            {
                await Print($"****************滑块异常****************");
                throw;
            }
        }

        /// <summary>
        /// 检查是否出现登录验证码页面
        /// </summary>
        async Task CheckVerification(IPage page)
        {
            var Verification = await CheckElement(page, Player.GetPath("登录验证页面"));
            if (Verification == null) return;
            var Verification_input = await Verification.QuerySelectorAsync(Player.GetPath("登录验输入框"));
            if (Verification_input == null) return;
            await Print($"检查到 登录验证页面");
            string info = 
                 "*******************************************" +
                $"{Player.User} 登录时需要输入验证码！请先使用浏览器模式登录后再从新发布任务！\n" +
                $"如您已经是浏览器模式，【那么请处理完验证码页面后，进入到我的宝贝界面】，\n" +
                $"【再点击确定按钮】，脚本将会继续运行！\n" +
                $"*******************************************";
            await Print(info);
            MessageBox.Show(info);
        }

        /// <summary>
        /// 保存登录记录
        /// </summary>
        /// <param name="save">0不保存</param>
        async Task SaveStorage(int save)
        {
            if (save == 0) return;
            // 保存登录记录
            await Print($"保存登录记录: {Player.OtherName}.json");
            var bcssOptions = new BrowserContextStorageStateOptions { Path = $"{Player.OtherName}.json" };
            await myContext.StorageStateAsync(bcssOptions);
        }

        /// <summary>
        /// 判断指定元素是否存在
        /// </summary>
        /// <param name="page">元素所在的页面对象</param>
        /// <param name="path">元素定位表达式</param>
        /// <returns>存在返回true</returns>
        async Task<IElementHandle> CheckElement(IPage page, string path)
        {
            try
            {
                IElementHandle elements = await page.QuerySelectorAsync(path);
                return elements;
            }
            catch (Exception e)
            {
                await Print(e.StackTrace);
                throw;
            }
        }

        /// <summary>
        /// 获取元素位置对象
        /// </summary>
        async Task<LocatorBoundingBoxResult> GetElementBOX(IPage page,string path)
        {
            try
            {
                var elementBox = page.Locator(path);
                return await elementBox.BoundingBoxAsync();
            }
            catch (Exception e)
            {
                await Print(e.StackTrace);
                throw;
            }
        }

        /// <summary>
        /// 执行关闭操作
        /// </summary>
        async Task Closed()
        {
            await Print("关闭 pages");
            foreach (var page in pages)
            {
                if (!page.IsClosed) 
                { 
                    await page.CloseAsync(); 
                }
                    
            }
            await Print("关闭 context");
            if (myContext != null) 
            { 
                await myContext.CloseAsync(); 
            }
        }

        /// <summary>
        /// 对当前页进行截图
        /// </summary>
        /// <param name="pathName">图片名称</param>
        async Task Screenshot(IPage page, string pathName)
        {
            string info =
                $"*********************** {pathName} ***********************\n" +
                $"{Player.ErrPicPath}\\{pathName}.png\n" +
                $"{Player.ErrTxtPath}\n" +
                $"*********************** {pathName} ***********************";

            await Print(info);

            await page.ScreenshotAsync(new PageScreenshotOptions { Path = $"{Player.ErrPicPath}\\{pathName}.png" });
            if (!File.Exists(Player.ErrTxtPath)) { File.WriteAllText(Player.ErrTxtPath, ""); }
        }

        async Task Navigation(NavigationUtils navigationUtils, IPage page, string request, string execute, string elementKey)
        {
            try
            {
                isOk = await navigationUtils.NavigationGo(request, execute, Player.GetPath(elementKey));
                if (!isOk) { await Closed(); return; }
                if (Player.MyStatus.Status) { await Closed(); return; }
            }
            catch (Exception e)
            {
                await Screenshot(page, $"{elementKey}_异常");
                await Closed();
                await Print(e.StackTrace);
                throw;
            }
        }

        /// <summary>
        /// 输出日志内容
        /// </summary>
        async Task Print(string text)
        {
            await Task.Run(() =>
            {
                Player.DetailedAction(text);
            });
        }

        /// <summary>
        /// 等待指定秒数
        /// </summary>
        async Task Sleep(int second)
        {
            await Task.Run(() =>
            {
                Thread.Sleep(second * 1000);
            });
        }
    }
}
